Module to deal with river and basin morphology
!! Module to deal with river and basin morphology !|author: <a href="mailto:giovanni.ravazzani@polimi.it">Giovanni Ravazzani</a> ! license: <a href="http://www.gnu.org/licenses/">GPL</a> ! !### History ! ! current version 1.2 - 22nd April 2024 ! ! | version | date | comment | ! |----------|--------------|----------| ! | 0.1 | 17/Aug/2009 | Original code | ! | 0.2 | 23/Oct/2010 | Compute flow accumulation grid | ! | 0.3 | 04/Dec/2012 | Compute longest flow length | ! | 0.4 | 03/Apr/2013 | Derive aspect subroutine | ! | 0.5 | 10/Apr/2013 | Derive curvature subroutine | ! | 0.6 | 13/Apr/2013 | Fixed DeriveSlope to match ArcGis algorithm | ! | 0.7 | 16/Apr/2013 | DrainageDensity rooutine | ! | 0.8 | 03/May/2013 | TopographicIndex routine | ! | 0.9 | 10/Jun/2013 | CurvatureMicroMet routine | ! | 1.0 | 09/Oct/2013 | LongestFlowPathSlope | ! | 1.1 | 03/Feb/2021 | Compute Centroid | ! | 1.2 | 22/Apr/2024 | ESRI and GRASS flow direction can be used | ! ! !### License ! license: GNU GPL <http://www.gnu.org/licenses/> ! ! This file is part of ! ! MOSAICO -- MOdular library for raSter bAsed hydrologIcal appliCatiOn. ! ! Copyright (C) 2011 Giovanni Ravazzani ! !### Code Description ! Language: Fortran 90. ! ! Software Standards: "European Standards for Writing and ! Documenting Exchangeable Fortran 90 Code". ! !### Module Description ! library to deal with river and basin morphology MODULE Morphology ! Modules used: USE DataTypeSizes, ONLY: & !Imported parameters: short, & long, & float USE GridLib, ONLY: & !Imported type definitions: grid_integer, & grid_real, & ! Imported routines: NewGrid, & GridDestroy, & ExportGrid, & !Imported parameters: ESRI_ASCII, & ESRI_BINARY, & NET_CDF USE GridOperations, ONLY: & !Imported routines: IsOutOfGrid, & GetIJ, & CellArea, & GetXY, & GridResample, & ExtractBorder, & CellLength, & ASSIGNMENT(=) USE GeoLib, ONLY: & !Imported definitions: Coordinate, & !Imported variables: point1, & point2, & !Imported routines: Distance, & ASSIGNMENT (=) USE LogLib, ONLY: & !Imported routines: Catch USE Units, ONLY: & !Imported parameters: pi USE StringManipulation, ONLY: & !Imported routines: ToString, & StringToLower IMPLICIT NONE PUBLIC :: HortonOrders PUBLIC :: CheckOutlet PUBLIC :: DownstreamCell PUBLIC :: FlowAccumulation PUBLIC :: DeriveSlope PUBLIC :: DeriveAspect PUBLIC :: DeriveCurvature PUBLIC :: CurvatureMicroMet PUBLIC :: BasinDelineate PUBLIC :: CellIsSpring PUBLIC :: LongestFlowLength PUBLIC :: LongestFlowPathSlope PUBLIC :: Perimeter PUBLIC :: Centroid PUBLIC :: SetFlowDirectionConvention PRIVATE :: ConfluenceIsAround PRIVATE :: BasinMask PRIVATE :: BasinArea PRIVATE :: CentroidGridReal PRIVATE :: CentroidGridInteger INTERFACE Centroid MODULE PROCEDURE CentroidGridReal MODULE PROCEDURE CentroidGridInteger END INTERFACE !flow direction INTEGER (KIND = short) :: NW !! North-West INTEGER (KIND = short) :: N !! North INTEGER (KIND = short) :: NE !! North-East INTEGER (KIND = short) :: W !! West INTEGER (KIND = short) :: E !! East INTEGER (KIND = short) :: SW !! South-West INTEGER (KIND = short) :: S !! South INTEGER (KIND = short) :: SE !! South-East !flow direction conventions INTEGER (KIND = short), PARAMETER :: ESRI = 1, GRASS = 2 INTEGER (KIND = short) :: flowDirectionConvention !======= CONTAINS !======= ! Define procedures contained in this module. !============================================================================== !| Description: ! Set flow direction convention: ! ! - ESRI: NW=32, N=64, NE=128, W=16, E=1, SW=8, S=4, SE=2 ! - GRASS: NW=3, N=2, NE=1, W=4, E=8, SW=5, S=6, SE=7 ! SUBROUTINE SetFlowDirectionConvention & ! (string) IMPLICIT NONE !Arguments with intent in: CHARACTER (LEN = *), INTENT (IN) :: string !-------------------------------end of declarations---------------------------- SELECT CASE ( TRIM (StringToLower(string) ) ) CASE ('esri') flowDirectionConvention = ESRI NW = 32; N = 64; NE = 128; W = 16; E = 1; SW = 8; S = 4; SE = 2 CASE ('grass') flowDirectionConvention = GRASS NW = 3; N = 2; NE = 1; W = 4; E = 8; SW = 5; S = 6; SE = 7 CASE DEFAULT CALL Catch ('error', 'Morphology', 'flow direction convention not found: ', & argument = TRIM (string) ) END SELECT RETURN END SUBROUTINE SetFlowDirectionConvention !============================================================================== !| Description: ! returns the coordinate of the centroid given a `grid_integer` mask, ! in the same coordinate reference system of `grid_integer` SUBROUTINE CentroidGridInteger & ! (grid, ccoord) IMPLICIT NONE !Arguments with intent in: TYPE (grid_integer), INTENT (IN) :: grid !!input grid !Arguments with intent out or inout TYPE (Coordinate), INTENT (OUT) :: ccoord !! coordinate of computed centroid !local declarations: INTEGER (KIND = short) :: cj, ci !!coordinate INTEGER (KIND = short) :: i, j INTEGER (KIND = short) :: countcell !--------------------------------end of declaration---------------------------- !set coordinate reference system of centroid ccoord % system = grid % grid_mapping cj = 0 ci = 0 countcell = 0 DO i = 1, grid % idim DO j = 1, grid % jdim IF (grid % mat (i,j) /= grid % nodata) THEN countcell = countcell + 1 cj = cj + j ci = ci + i END IF END DO END DO IF ( countcell > 0) THEN cj = cj / countcell ci = ci / countcell ELSE CALL Catch ('error', 'Morphology', 'area zero found while computing centroid coordinates') END IF CALL GetXY (ci, cj, grid, ccoord % easting, ccoord % northing ) RETURN END SUBROUTINE CentroidGridInteger !============================================================================== !| Description: ! returns the coordinate of the centroid given a `grid_integer` mask, ! in the same coordinate reference system of `grid_integer` SUBROUTINE CentroidGridReal & ! (grid, ccoord) IMPLICIT NONE !Arguments with intent in: TYPE (grid_real), INTENT (IN) :: grid !!input grid !Arguments with intent out or inout TYPE (Coordinate), INTENT (OUT) :: ccoord !! coordinate of computed centroid !local declarations: INTEGER (KIND = short) :: cj, ci !!coordinate INTEGER (KIND = short) :: i, j INTEGER (KIND = short) :: countcell !--------------------------------end of declaration---------------------------- !set coordinate reference system to centroid ccoord % system = grid % grid_mapping cj = 0 ci = 0 countcell = 0 DO i = 1, grid % idim DO j = 1, grid % jdim IF (grid % mat (i,j) /= grid % nodata) THEN countcell = countcell + 1 cj = cj + j ci = ci + i END IF END DO END DO IF ( countcell > 0) THEN cj = cj / countcell ci = ci / countcell ELSE CALL Catch ('error', 'Morphology', 'area zero found while computing centroid coordinates') END IF CALL GetXY (ci, cj, grid, ccoord % easting, ccoord % northing ) RETURN END SUBROUTINE CentroidGridReal !============================================================================== !| Description: ! returns a grid_integer containing Horton orders. Horton orders are ! computed on the entire space-filled basin. SUBROUTINE HortonOrders & ! (flowDirection,orders,basinOrder) IMPLICIT NONE !Arguments with intent in: TYPE (grid_integer), INTENT (IN) :: flowDirection !Arguments with intent out or inout TYPE (grid_integer), INTENT (INOUT) :: orders INTEGER, OPTIONAL, INTENT (OUT) :: basinOrder !!the maximum order of the basin !local declarations: LOGICAL :: confluence !!true if confluence LOGICAL :: outlet !!true if basin outlet INTEGER :: row, col !!current cell INTEGER :: iDown, jDown !!downstream cell INTEGER :: numConf !!number of confluences INTEGER :: order !! Horton order INTEGER :: cellsCount INTEGER :: i, j !--------------------------------end of declaration---------------------------- order = 1 numConf = 1 DO WHILE (numConf > 0) ! se non trovo confluenze ! di classe order ! l'operazione รจ terminata CALL Catch ('info', 'Morphology', 'Elaborating reaches of stream order: ', & argument = ToString(order)) numConf = 0 !-----follow the reach till a confluence or a basin outlet------ DO j = 1,orders % jdim DO i = 1,orders % idim IF(CellIsSpring(i,j,flowDirection)) THEN !found a spring row = i col = j outlet = .FALSE. confluence = .FALSE. cellsCount = 0 orders % mat(i,j) = 1 DO WHILE (.NOT. outlet) ! follow the reach till the basin outlet IF (orders % mat(row,col) == order ) THEN cellsCount = cellsCount + 1 ENDIF CALL DownstreamCell(row, col, & flowDirection%mat(row,col), & iDown, jDown) IF (cellsCount >= 1 ) THEN !I am in the reach of that order !check if downstream cell is a confluence to increment horton order !Downstream the confluence, till the basin outlet, as temptative value, !order is increased by 1 (order + 1) IF ( .NOT. confluence ) THEN CALL ConfluenceIsAround (iDown, jDown, row, col, & flowDirection,confluence,orders,order) IF(confluence) numConf = numConf + 1 ENDIF outlet = CheckOutlet (row,col,iDown,jDown,flowDirection) IF (.NOT. outlet) THEN IF (.NOT. confluence) THEN orders % mat(iDown,jDown) = order ELSE orders % mat(iDown,jDown) = order + 1 ENDIF ENDIF ENDIF ! cellsCount >= 1 outlet = CheckOutlet(row,col,iDown,jDown,flowDirection) !loop row = iDown col = jDown END DO ENDIF ENDDO ENDDO !ciclo sulla matrice ordini !------------------------------------------------------------------------------ order = order + 1 ENDDO IF ( PRESENT (basinOrder) ) THEN basinOrder = order - 1 END IF END SUBROUTINE HortonOrders !============================================================================== !| Description: ! Scan the eigth cells surrounding the center cell (row,col) (neglecting ! the upstream cell (i,j) ) to find if a confluence is present. ! The confluence cell must be of the same order or not yet defined. SUBROUTINE ConfluenceIsAround & ! (row,col,i,j,flowDir,confluence,orders,Norder) IMPLICIT NONE TYPE (grid_integer), INTENT(IN):: flowDir TYPE (grid_integer), INTENT(IN):: orders INTEGER, INTENT(IN):: i,j,row,col,Norder LOGICAL, INTENT(OUT):: confluence !----------------------------------------------------------- IF(.NOT. IsOutOfGrid(row,col+1,flowDir) ) THEN IF(flowDir%mat(row,col+1) == W .AND.& (row /= i .OR. col+1 /= j).AND. & (orders%mat(row,col+1) == orders%nodata.OR. & orders%mat(row,col+1) == Norder) ) THEN confluence = .TRUE. ENDIF ENDIF IF(.NOT. IsOutOfGrid(row+1,col+1,flowDir) ) THEN IF(flowDir%mat(row+1,col+1) == NW .AND.& (row+1 /=i .OR. col+1 /= j).AND. & (orders%mat(row+1,col+1) == orders%nodata.OR. & orders%mat(row+1,col+1) == Norder) ) THEN confluence = .TRUE. ENDIF ENDIF IF(.NOT. IsOutOfGrid(row+1,col,flowDir) ) THEN IF(flowDir%mat(row+1,col) == N .AND.& (row+1 /=i .OR. col /= j).AND. & (orders%mat(row+1,col) == orders%nodata.OR. & orders%mat(row+1,col) == Norder) ) THEN confluence = .TRUE. ENDIF ENDIF IF(.NOT. IsOutOfGrid(row+1,col-1,flowDir) ) THEN IF(flowDir%mat(row+1,col-1) == NE .AND.& (row+1 /= i .OR. col-1 /= j).AND. & (orders%mat(row+1,col-1) == orders%nodata.OR. & orders%mat(row+1,col-1) == Norder) ) THEN confluence = .TRUE. ENDIF ENDIF IF(.NOT. IsOutOfGrid(row,col-1,flowDir) ) THEN IF(flowDir%mat(row,col-1) == E .AND.& (row /= i .OR. col-1 /= j).AND. & (orders%mat(row,col-1) == orders%nodata.OR. & orders%mat(row,col-1) == Norder) ) THEN confluence = .TRUE. ENDIF ENDIF IF(.NOT. IsOutOfGrid(row-1,col-1,flowDir) ) THEN IF(flowDir%mat(row-1,col-1) == SE .AND.& (row-1 /= i .OR. col-1 /= j).AND. & (orders%mat(row-1,col-1) == orders%nodata.OR. & orders%mat(row-1,col-1) == Norder) ) THEN confluence = .TRUE. ENDIF ENDIF IF(.NOT. IsOutOfGrid(row-1,col,flowDir) ) THEN IF(flowDir%mat(row-1,col) == S .AND.& (row-1 /= i .OR. col /= j).AND. & (orders%mat(row-1,col) == orders%nodata.OR. & orders%mat(row-1,col) == Norder) ) THEN confluence = .TRUE. ENDIF ENDIF IF(.NOT. IsOutOfGrid(row-1,col+1,flowDir) ) THEN IF(flowDir%mat(row-1,col+1) == SW .AND.& (row-1 /= i .OR. col+1 /= j).AND. & (orders%mat(row-1,col+1) == orders%nodata.OR. & orders%mat(row-1,col+1) == Norder) ) THEN confluence = .TRUE. ENDIF ENDIF RETURN END SUBROUTINE ConfluenceIsAround !============================================================================== !| Description: ! if the downstream cell is a nodata value or out of grid space, ! or points toward the current cell, the current cell ! is the basin outlet. LOGICAL FUNCTION CheckOutlet & ! (icurrent, jcurrent, iDown, jDown, flowDirection) IMPLICIT NONE INTEGER, INTENT(in) :: icurrent INTEGER, INTENT(in) :: jcurrent INTEGER, INTENT(in) :: iDown !riga cella di valle INTEGER, INTENT(in) :: jDown !colonna cella di valle TYPE(grid_integer), INTENT(in):: flowDirection !local declarations: INTEGER :: iback, jback !------------------------------end of declaration ----------------------------- CheckOutlet = .FALSE. !first case IF ( IsOutOfGrid (iDown,jDown,flowDirection) ) THEN CheckOutlet = .TRUE. RETURN END IF !second case IF ( flowDirection % mat(iDown,jDown) == flowDirection % nodata) THEN CheckOutlet = .TRUE. RETURN END IF !third case CALL DownstreamCell (iDown,jDown,flowDirection%mat(iDown,jDown),iback,jback) IF ( iback == icurrent .AND. jback == jcurrent) THEN CheckOutlet = .TRUE. RETURN END IF RETURN END FUNCTION CheckOutlet !============================================================================== !| Description: ! returns the position (is,js) of the downstream cell and, optionally, ! the flow path length, considering cardinal and diagonal direction SUBROUTINE DownstreamCell & ! (iin, jin, dir, is, js, dx, grid) IMPLICIT NONE !Arguments with intent in: INTEGER (KIND = short), INTENT (IN) :: iin, jin !!current cell INTEGER (KIND = long), INTENT (IN) :: dir !!flow direction TYPE(grid_integer), INTENT (IN),OPTIONAL:: grid !!used to define coordinate reference system !Arguments with intent out: INTEGER (KIND = short), INTENT (OUT) :: is,js !!downstream cell REAL (KIND = float), INTENT (OUT) ,OPTIONAL :: dx !!flow path length [m] !Local declarations REAL (KIND = float) :: ddx,x,y,xs,ys !--------------------end of declarations--------------------------------------- !east IF ( dir == E ) THEN js = jin + 1 is = iin END IF !south east IF ( dir == SE ) THEN js = jin + 1 is = iin + 1 END IF !south IF ( dir == S ) THEN js = jin is = iin + 1 END IF !south west IF ( dir == SW ) THEN js = jin - 1 is = iin + 1 END IF !west IF ( dir == W ) THEN js = jin - 1 is = iin END IF !north west IF ( dir == NW ) THEN js = jin - 1 is = iin - 1 END IF !north IF ( dir == N ) THEN js = jin is = iin - 1 END IF !north easth IF ( dir == NE ) THEN js = jin + 1 is = iin - 1 END IF IF ((PRESENT(dx)).and.(PRESENT(grid))) THEN CALL GetXY (iin,jin,grid,x,y) CALL GetXY (is,js,grid,xs,ys) point1 % system = grid % grid_mapping point2 % system = grid % grid_mapping point1 % northing = y point1 % easting = x point2 % northing = ys point2 % easting = xs ddx = distance(point1,point2) dx = ddx RETURN ELSE IF ((PRESENT(dx)) .AND. .NOT. (PRESENT(grid))) THEN CALL Catch ('error', 'Morphology', 'missing grid while calculating downstream distance') END IF END SUBROUTINE DownstreamCell !============================================================================== !| Description: ! returns the position (is,js) of the upstream cell and, optionally, ! the flow path length, considering cardinal and diagonal direction SUBROUTINE UpstreamCell & ! (i, j, flowdir, flowacc, is, js, found, dx) IMPLICIT NONE !Arguments with intent in: INTEGER (KIND = short), INTENT (IN) :: i, j !!current cell TYPE (grid_integer), INTENT (IN) :: flowdir !!flow direction map TYPE (grid_real), INTENT (IN) :: flowacc !! flow accumulation map !Arguments with intent out: INTEGER (KIND = short), INTENT (OUT) :: is,js !!upstream cell LOGICAL, INTENT(OUT) :: found !! upsteam cell found REAL (KIND = float), INTENT (OUT) ,OPTIONAL :: dx !!flow path length [m] !Local declarations REAL (KIND = float) :: x,y,xs,ys INTEGER (KIND = short) :: row, col REAL (KIND = float) :: minDeltaArea, deltaArea !--------------------end of declarations--------------------------------------- minDeltaArea = 1000000000. is = 0 js = 0 found = .FALSE. !east cell row = i col = j + 1 IF(.NOT. IsOutOfGrid(row,col,flowDir) ) THEN IF(flowDir%mat(row,col) == W ) THEN !check delta area deltaArea = flowacc % mat (i, j) - flowacc % mat (row,col) IF ( deltaArea > 0. .AND. deltaArea < minDeltaArea ) THEN found = .TRUE. minDeltaArea = deltaArea is = row js = col END IF ENDIF ENDIF !south-east cell row = i + 1 col = j + 1 IF(.NOT. IsOutOfGrid(row,col,flowDir) ) THEN IF(flowDir%mat(row,col) == NW ) THEN !check delta area deltaArea = flowacc % mat (i, j) - flowacc % mat (row,col) IF ( deltaArea > 0. .AND. deltaArea < minDeltaArea ) THEN found = .TRUE. minDeltaArea = deltaArea is = row js = col END IF ENDIF ENDIF !south cell row = i + 1 col = j IF(.NOT. IsOutOfGrid(row,col,flowDir) ) THEN IF(flowDir%mat(row,col) == N ) THEN !check delta area deltaArea = flowacc % mat (i, j) - flowacc % mat (row,col) IF ( deltaArea > 0. .AND. deltaArea < minDeltaArea ) THEN found = .TRUE. minDeltaArea = deltaArea is = row js = col END IF ENDIF ENDIF !south-west cell row = i + 1 col = j - 1 IF(.NOT. IsOutOfGrid(row,col,flowDir) ) THEN IF(flowDir%mat(row,col) == NE ) THEN !check delta area deltaArea = flowacc % mat (i, j) - flowacc % mat (row,col) IF ( deltaArea > 0. .AND. deltaArea < minDeltaArea ) THEN found = .TRUE. minDeltaArea = deltaArea is = row js = col END IF ENDIF ENDIF !west cell row = i col = j - 1 IF(.NOT. IsOutOfGrid(row,col,flowDir) ) THEN IF(flowDir%mat(row,col) == E ) THEN !check delta area deltaArea = flowacc % mat (i, j) - flowacc % mat (row,col) IF ( deltaArea > 0. .AND. deltaArea < minDeltaArea ) THEN found = .TRUE. minDeltaArea = deltaArea is = row js = col END IF ENDIF ENDIF !north-west cell row = i - 1 col = j - 1 IF(.NOT. IsOutOfGrid(row,col,flowDir) ) THEN IF(flowDir%mat(row,col) == SE ) THEN !check delta area deltaArea = flowacc % mat (i, j) - flowacc % mat (row,col) IF ( deltaArea > 0. .AND. deltaArea < minDeltaArea ) THEN found = .TRUE. minDeltaArea = deltaArea is = row js = col END IF ENDIF ENDIF !north cell row = i - 1 col = j IF(.NOT. IsOutOfGrid(row,col,flowDir) ) THEN IF(flowDir%mat(row,col) == S ) THEN !check delta area deltaArea = flowacc % mat (i, j) - flowacc % mat (row,col) IF ( deltaArea > 0. .AND. deltaArea < minDeltaArea ) THEN found = .TRUE. minDeltaArea = deltaArea is = row js = col END IF ENDIF ENDIF !north-east cell row = i - 1 col = j + 1 IF(.NOT. IsOutOfGrid(row,col,flowDir) ) THEN IF(flowDir%mat(row,col) == SW ) THEN !check delta area deltaArea = flowacc % mat (i, j) - flowacc % mat (row,col) IF ( deltaArea > 0. .AND. deltaArea < minDeltaArea ) THEN found = .TRUE. minDeltaArea = deltaArea is = row js = col END IF ENDIF ENDIF IF (found) THEN IF (PRESENT (dx) ) THEN CALL GetXY (i,j,flowDir,x,y) CALL GetXY (is,js,flowDir,xs,ys) point1 % system = flowDir % grid_mapping point2 % system = flowDir % grid_mapping point1 % northing = y point1 % easting = x point2 % northing = ys point2 % easting = xs dx = distance(point1,point2) END IF END IF END SUBROUTINE UpstreamCell !============================================================================== !| Description: ! For each cell, the routine calculates the maximum rate of change in value ! from that cell to its neighbors. Basically, the maximum change in ! elevation over the distance between the cell and its eight neighbors ! identifies the steepest downhill descent from the cell. ! Conceptually, the routine fits a plane to the z-values of a 3 x 3 cell ! neighborhood around the processing or center cell. The slope value of this ! plane is calculated using the average maximum technique. The direction ! the plane faces is the aspect for the processing cell. ! If there is a cell location in the neighborhood with a NoData z-value, ! the z-value of the center cell will be assigned to the location. ! ! References: ! ! Burrough, P. A., and McDonell, R. A., 1998. Principles of Geographical ! Information Systems (Oxford University Press, New York), 190 pp. SUBROUTINE DeriveSlope & ! (dem, slope) IMPLICIT NONE !arguments with intent in TYPE(grid_real), INTENT(in):: dem !arguments with intent out TYPE (grid_real), INTENT (out) :: slope ![rad] !local variables INTEGER :: ii,jj REAL (KIND = float) :: dZdX !!rate of change in the x direction REAL (KIND = float) :: dZdY !!rate of change in the y direction REAL (KIND = float) :: a !!elevation of N-W cell REAL (KIND = float) :: b !!elevation of N cell REAL (KIND = float) :: c !!elevation of N-E cell REAL (KIND = float) :: d !!elevation of W cell REAL (KIND = float) :: f !!elevation of E cell REAL (KIND = float) :: g !!elevation of S-W cell REAL (KIND = float) :: h !!elevation of S cell REAL (KIND = float) :: i !!elevation of S-E cell REAL (KIND = float) :: L !!length !------------------------------end of declaration ----------------------------- !allocate slope grid CALL NewGrid (slope,dem) !loop through dem grid idim: DO ii = 1, dem % idim jdim: DO jj = 1, dem % jdim IF (dem % mat(ii,jj) /= dem % nodata) THEN !set cell size L = CellArea (dem,ii,jj) ** 0.5 !north-west cell IF ( .NOT. IsOutOfGrid (ii-1,jj-1,dem) ) THEN IF ( dem % mat (ii-1,jj-1) /= dem % nodata) THEN a = dem % mat (ii-1,jj-1) ELSE a = dem % mat (ii,jj) END IF ELSE a = dem % mat (ii,jj) END IF !north cell IF ( .NOT. IsOutOfGrid (ii-1,jj,dem) ) THEN IF ( dem % mat (ii-1,jj) /= dem % nodata) THEN b = dem % mat (ii-1,jj) ELSE b = dem % mat (ii,jj) END IF ELSE b = dem % mat (ii,jj) END IF !north-east cell IF ( .NOT. IsOutOfGrid (ii-1,jj+1,dem) ) THEN IF ( dem % mat (ii-1,jj+1) /= dem % nodata) THEN c = dem % mat (ii-1,jj+1) ELSE c = dem % mat (ii,jj) END IF ELSE c = dem % mat (ii,jj) END IF !west cell IF ( .NOT. IsOutOfGrid (ii,jj-1,dem) ) THEN IF ( dem % mat (ii,jj-1) /= dem % nodata) THEN d = dem % mat (ii,jj-1) ELSE d = dem % mat (ii,jj) END IF ELSE d = dem % mat (ii,jj) END IF !east cell IF ( .NOT. IsOutOfGrid (ii,jj+1,dem) ) THEN IF ( dem % mat (ii,jj+1) /= dem % nodata) THEN f = dem % mat (ii,jj+1) ELSE f = dem % mat (ii,jj) END IF ELSE f = dem % mat (ii,jj) END IF !south-west cell IF ( .NOT. IsOutOfGrid (ii+1,jj-1,dem) ) THEN IF ( dem % mat (ii+1,jj-1) /= dem % nodata) THEN g = dem % mat (ii+1,jj-1) ELSE g = dem % mat (ii,jj) END IF ELSE g = dem % mat (ii,jj) END IF !south cell IF ( .NOT. IsOutOfGrid (ii+1,jj,dem) ) THEN IF ( dem % mat (ii+1,jj) /= dem % nodata) THEN h = dem % mat (ii+1,jj) ELSE h = dem % mat (ii,jj) END IF ELSE h = dem % mat (ii,jj) END IF !south-east cell IF ( .NOT. IsOutOfGrid (ii+1,jj+1,dem) ) THEN IF ( dem % mat (ii+1,jj+1) /= dem % nodata) THEN i = dem % mat (ii+1,jj+1) ELSE i = dem % mat (ii,jj) END IF ELSE i = dem % mat (ii,jj) END IF !compute the rate of change in the x direction dZdX = ((c + 2.*f + i) - (a + 2.*d + g)) / (8. * L) !compute the rate of change in the y direction dZdY = ((g + 2.*h + i) - (a + 2.*b + c)) / (8. * L) !compute slope slope % mat (ii,jj) = ATAN ( (dZdX ** 2. + dZdY ** 2.) ** 0.5 ) END IF END DO jdim END DO idim RETURN END SUBROUTINE DeriveSlope !============================================================================== !| Description: ! Aspect identifies the downslope direction of the maximum rate of change ! in value from each cell to its neighbors. ! Aspect can be thought of as the slope direction. The values of the output ! raster will be the compass direction of the aspect. ! A moving window visits each cell in the input raster and for each cell ! in the center of the window, an aspect value is calculated using an ! algorithm that incorporates the values of the cell's eight neighbors. ! If any neighborhood cells are NoData, they are first assigned the value ! of the center cell, then the aspect is computed. ! The cells are identified as letters 'a' to 'i', with 'e' representing the ! cell for which the aspect is being calculated. ! The rate of change in the x direction for cell 'e' is calculated with ! the following algorithm: !``` ! [dz/dx] = ((c + 2f + i) - (a + 2d + g)) / 8 !``` ! The rate of change in the y direction for cell 'e' is calculated ! with the following algorithm: !``` ! [dz/dy] = ((g + 2h + i) - (a + 2b + c)) / 8 !``` ! Taking the rate of change in both the x and y direction for ! cell 'e', aspect is calculated using: !``` ! aspect = 57.29578 * atan2 ([dz/dy], -[dz/dx]) !``` ! The aspect value is then converted to compass direction values ! (0โ2pi rad), according to the following rule: !``` ! if aspect < 0 ! ! cell = pi/2 - aspect ! ! else if aspect > pi/2 ! ! cell = 2pi - aspect + pi/2 ! ! else ! ! cell = pi/2 - aspect !``` ! Flat areas in the input rasterโareas where the slope is zero are assigned ! an aspect of -1. ! ! Reference: ! ! Burrough, P. A. and McDonell, R.A., 1998. Principles of Geographical ! Information Systems (Oxford University Press, New York), p. 190 SUBROUTINE DeriveAspect & ! (dem, aspect) IMPLICIT NONE !arguments with intent in TYPE(grid_real), INTENT(in):: dem !arguments with intent out TYPE (grid_real), INTENT (out) :: aspect !(rad) !local variables INTEGER :: ii,jj REAL (KIND = float) :: dZdX !!rate of change in the x direction REAL (KIND = float) :: dZdY !!rate of change in the y direction REAL (KIND = float) :: aspectIJ REAL (KIND = float) :: a !!elevation of N-W cell REAL (KIND = float) :: b !!elevation of N cell REAL (KIND = float) :: c !!elevation of N-E cell REAL (KIND = float) :: d !!elevation of W cell REAL (KIND = float) :: f !!elevation of E cell REAL (KIND = float) :: g !!elevation of S-W cell REAL (KIND = float) :: h !!elevation of S cell REAL (KIND = float) :: i !!elevation of S-E cell !------------------------------end of declaration ----------------------------- !allocate aspect grid CALL NewGrid (aspect,dem) !loop through dem grid idim: DO ii = 1, dem % idim jdim: DO jj = 1, dem % jdim IF (dem % mat(ii,jj) /= dem % nodata) THEN !north-west cell IF ( .NOT. IsOutOfGrid (ii-1,jj-1,dem) ) THEN IF ( dem % mat (ii-1,jj-1) /= dem % nodata) THEN a = dem % mat (ii-1,jj-1) ELSE a = dem % mat (ii,jj) END IF ELSE a = dem % mat (ii,jj) END IF !north cell IF ( .NOT. IsOutOfGrid (ii-1,jj,dem) ) THEN IF ( dem % mat (ii-1,jj) /= dem % nodata) THEN b = dem % mat (ii-1,jj) ELSE b = dem % mat (ii,jj) END IF ELSE b = dem % mat (ii,jj) END IF !north-east cell IF ( .NOT. IsOutOfGrid (ii-1,jj+1,dem) ) THEN IF ( dem % mat (ii-1,jj+1) /= dem % nodata) THEN c = dem % mat (ii-1,jj+1) ELSE c = dem % mat (ii,jj) END IF ELSE c = dem % mat (ii,jj) END IF !west cell IF ( .NOT. IsOutOfGrid (ii,jj-1,dem) ) THEN IF ( dem % mat (ii,jj-1) /= dem % nodata) THEN d = dem % mat (ii,jj-1) ELSE d = dem % mat (ii,jj) END IF ELSE d = dem % mat (ii,jj) END IF !east cell IF ( .NOT. IsOutOfGrid (ii,jj+1,dem) ) THEN IF ( dem % mat (ii,jj+1) /= dem % nodata) THEN f = dem % mat (ii,jj+1) ELSE f = dem % mat (ii,jj) END IF ELSE f = dem % mat (ii,jj) END IF !south-west cell IF ( .NOT. IsOutOfGrid (ii+1,jj-1,dem) ) THEN IF ( dem % mat (ii+1,jj-1) /= dem % nodata) THEN g = dem % mat (ii+1,jj-1) ELSE g = dem % mat (ii,jj) END IF ELSE g = dem % mat (ii,jj) END IF !south cell IF ( .NOT. IsOutOfGrid (ii+1,jj,dem) ) THEN IF ( dem % mat (ii+1,jj) /= dem % nodata) THEN h = dem % mat (ii+1,jj) ELSE h = dem % mat (ii,jj) END IF ELSE h = dem % mat (ii,jj) END IF !south-east cell IF ( .NOT. IsOutOfGrid (ii+1,jj+1,dem) ) THEN IF ( dem % mat (ii+1,jj+1) /= dem % nodata) THEN i = dem % mat (ii+1,jj+1) ELSE i = dem % mat (ii,jj) END IF ELSE i = dem % mat (ii,jj) END IF !compute the rate of change in the x direction dZdX = ((c + 2.*f + i) - (a + 2.*d + g)) / 8. !compute the rate of change in the y direction dZdY = ((g + 2.*h + i) - (a + 2.*b + c)) / 8. !compute aspect IF (dZdX == 0. .AND. dZdY == 0.) THEN aspect % mat (ii,jj) = -1. CYCLE ELSE aspectIJ = ATAN2 (dZdY, -dZdX) END IF !convert aspect value to compass direction values (0โ2Pi radians) IF (aspectIJ < 0) THEN aspect % mat (ii,jj) = pi/2. - aspectIJ ELSE IF (aspectIJ > pi /2.) THEN aspect % mat (ii,jj) = 2.*pi - aspectIJ + pi/2. ELSE aspect % mat (ii,jj) = pi/2. - aspectIJ END IF END IF END DO jdim END DO idim RETURN END SUBROUTINE DeriveAspect !============================================================================== !| Description: ! Calculate curvature according to method implemented in MICROMET ! ! References: ! ! Liston, G.E., Elder, K., A Meteorological Distribution System for ! High-Resolution Terrestrial Modeling, JOURNAL OF HYDROMETEOROLOGY, ! 7, 217-234, 2006. SUBROUTINE CurvatureMicroMet & ! (dem, curve_len_scale, curvature) IMPLICIT NONE !arguments with intent in TYPE(grid_real), INTENT(in) :: dem REAL (KIND = float), INTENT(in) :: curve_len_scale !arguments with intent out TYPE (grid_real), INTENT (out) :: curvature !local declarations: REAL (KIND = float) :: deltaxy REAL (KIND = float) :: z, zw, ze, zs, zn, zsw, zne, znw, zse REAL (KIND = float) :: curve_max INTEGER (KIND = short) :: inc INTEGER (KIND = short) :: i, j !-----------------------------end of declarations------------------------------ !allocate grid CALL NewGrid (curvature, dem) !compute curvature DO i = 1, dem % idim DO j = 1, dem % jdim IF (dem % mat (i,j) /= dem % nodata) THEN !average grid increment deltaxy = CellArea (dem,i,j) ** 0.5 !Convert the length scale to an appropriate grid increment. inc = max(1,nint(curve_len_scale/deltaxy)) !srt z z = dem % mat(i,j) !set zn IF ( .NOT. IsOutOfGrid (i-inc,j,dem) ) THEN IF ( dem % mat (i-inc,j) /= dem % nodata) THEN zn = dem % mat (i-inc,j) ELSE zn = dem % mat (i,j) END IF ELSE zn = dem % mat (i,j) END IF !set zne IF ( .NOT. IsOutOfGrid (i-inc,j+inc,dem) ) THEN IF ( dem % mat (i-inc,j+inc) /= dem % nodata) THEN zne = dem % mat (i-inc,j+inc) ELSE zne = dem % mat (i,j) END IF ELSE zne = dem % mat (i,j) END IF !set ze IF ( .NOT. IsOutOfGrid (i,j+inc,dem) ) THEN IF ( dem % mat (i,j+inc) /= dem % nodata) THEN ze = dem % mat (i,j+inc) ELSE ze = dem % mat (i,j) END IF ELSE ze = dem % mat (i,j) END IF !set zse IF ( .NOT. IsOutOfGrid (i+inc,j+inc,dem) ) THEN IF ( dem % mat (i+inc,j+inc) /= dem % nodata) THEN zse = dem % mat (i+inc,j+inc) ELSE zse = dem % mat (i,j) END IF ELSE zse = dem % mat (i,j) END IF !set zs IF ( .NOT. IsOutOfGrid (i+inc,j,dem) ) THEN IF ( dem % mat (i+inc,j) /= dem % nodata) THEN zs = dem % mat (i+inc,j) ELSE zs = dem % mat (i,j) END IF ELSE zs = dem % mat (i,j) END IF !set zsw IF ( .NOT. IsOutOfGrid (i+inc,j-inc,dem) ) THEN IF ( dem % mat (i+inc,j-inc) /= dem % nodata) THEN zsw = dem % mat (i+inc,j-inc) ELSE zsw = dem % mat (i,j) END IF ELSE zsw = dem % mat (i,j) END IF !set zw IF ( .NOT. IsOutOfGrid (i,j-inc,dem) ) THEN IF ( dem % mat (i,j-inc) /= dem % nodata) THEN zw = dem % mat (i,j-inc) ELSE zw = dem % mat (i,j) END IF ELSE zw = dem % mat (i,j) END IF !set znw IF ( .NOT. IsOutOfGrid (i-inc,j-inc,dem) ) THEN IF ( dem % mat (i-inc,j-inc) /= dem % nodata) THEN znw = dem % mat (i-inc,j-inc) ELSE znw = dem % mat (i,j) END IF ELSE znw = dem % mat (i,j) END IF !curvature curvature % mat(i,j) = (4.0 * z - znw - zse - zsw - zne ) / & (sqrt(2.0) * 16.0 * real(inc) * deltaxy) + & (4.0 * z - zs - zn - ze - zw) / & (16.0 * real(inc) * deltaxy) END IF END DO END DO ! Scale the curvature such that the max abs(curvature) has a value ! of abs(0.5). Include a 1 mm curvature in curve_max to prevent ! divisions by zero in flat terrain where the curvature is zero. curve_max = 0.0 + 0.001 DO i = 1, dem % idim DO j = 1, dem % jdim IF (dem % mat (i,j) /= dem % nodata) THEN curve_max = MAX(curve_max,ABS(curvature%mat(i,j))) END IF END DO END DO DO i = 1, dem % idim DO j = 1, dem % jdim IF (dem % mat (i,j) /= dem % nodata) THEN curvature%mat(i,j) = curvature%mat(i,j) / (2.0 * curve_max) END IF END DO END DO RETURN END SUBROUTINE CurvatureMicroMet !============================================================================== !| Description: ! Calculates the curvature [1/m] of a raster surface, optionally including ! profile and plan curvature. If any neighborhood cells are NoData, they ! are first assigned the value of the center cell. ! It calculates the second derivative value of the input surface on a ! cell-by-cell basis. For each cell, a fourth-order polynomial of the form: !``` ! Z = Axยฒyยฒ + Bxยฒy + Cxyยฒ + Dxยฒ + Eyยฒ + Fxy + Gx + Hy + I !``` ! is fit to a surface composed of a 3x3 window. !``` ! A = [(Z1 + Z3 + Z7 + Z9) / 4 - (Z2 + Z4 + Z6 + Z8) / 2 + Z5] / L4 ! B = [(Z1 + Z3 - Z7 - Z9) /4 - (Z2 - Z8) /2] / L3 ! C = [(-Z1 + Z3 - Z7 + Z9) /4 + (Z4 - Z6)] /2] / L3 ! D = [(Z4 + Z6) /2 - Z5] / L2 ! E = [(Z2 + Z8) /2 - Z5] / L2 ! F = (-Z1 + Z3 + Z7 - Z9) / 4L2 ! G = (-Z4 + Z6) / 2L ! H = (Z2 - Z8) / 2L ! I = Z5 ! ! curvature = -2 (E + D) ! ! profileCurvature = -2 (D G^2 + E H^2 + F G H) / (G^2 + H^2) ! ! planCurvature = -2 (D H^2 + E G^2 - F G H) / (G^2 + H^2) !``` ! Profile curvature is parallel to the direction of the maximum slope. ! A negative value indicates that the surface is upwardly convex at ! that cell. A positive profile indicates that the surface is upwardly ! concave at that cell. A value of zero indicates that the surface ! is linear. Planform curvature (commonly called plan curvature) is ! perpendicular to the direction of the maximum slope. A positive value ! indicates the surface is sidewardly convex at that cell. A negative plan ! indicates the surface is sidewardly concave at that cell. A value of ! zero indicates the surface is linear. Plan curvature relates to the ! convergence and divergence of flow across a surface. ! ! References: ! ! Moore, I. D., R. B. Grayson, and A. R. Landson. 1991. Digital Terrain ! Modelling: A Review of Hydrological, Geomorphological, and Biological ! Applications. Hydrological Processes 5: 3โ30. ! ! Zeverbergen, L. W., and C. R. Thorne. 1987. Quantitative Analysis of ! Land Surface Topography. Earth Surface Processes and Landforms 12: 47โ56. SUBROUTINE DeriveCurvature & ! (dem, curvature, profileCurvature, planCurvature) IMPLICIT NONE !arguments with intent in TYPE(grid_real), INTENT(in):: dem !arguments with intent out TYPE (grid_real), INTENT (out) :: curvature TYPE (grid_real), OPTIONAL, INTENT (out) :: profileCurvature TYPE (grid_real), OPTIONAL, INTENT (out) :: planCurvature !local variables INTEGER :: i,j REAL (KIND = float) :: D, E, F, G, H REAL (KIND = float) :: z1, z2, z3, z4, z5, z6, z7, z8, z9 REAL (KIND = float) :: L !-----------------------------end of declarations------------------------------ !allocate grids CALL NewGrid (curvature, dem) IF (PRESENT(profileCurvature)) THEN CALL NewGrid (profileCurvature, dem) END IF IF (PRESENT(planCurvature)) THEN CALL NewGrid (planCurvature, dem) END IF !loop through dem grid idim: DO i = 1, dem % idim jdim: DO j = 1, dem % jdim IF (dem % mat(i,j) /= dem % nodata) THEN !set length L = CellArea (dem,i,j) ** 0.5 !set z5 z5 = dem % mat (i,j) !set z2 IF ( .NOT. IsOutOfGrid (i-1,j,dem) ) THEN IF ( dem % mat (i-1,j) /= dem % nodata) THEN z2 = dem % mat (i-1,j) ELSE z2 = dem % mat (i,j) END IF ELSE z2 = dem % mat (i,j) END IF !set z3 IF ( .NOT. IsOutOfGrid (i-1,j+1,dem) ) THEN IF ( dem % mat (i-1,j+1) /= dem % nodata) THEN z3 = dem % mat (i-1,j+1) ELSE z3 = dem % mat (i,j) END IF ELSE z3 = dem % mat (i,j) END IF !set z6 IF ( .NOT. IsOutOfGrid (i,j+1,dem) ) THEN IF ( dem % mat (i,j+1) /= dem % nodata) THEN z6 = dem % mat (i,j+1) ELSE z6 = dem % mat (i,j) END IF ELSE z6 = dem % mat (i,j) END IF !set z9 IF ( .NOT. IsOutOfGrid (i+1,j+1,dem) ) THEN IF ( dem % mat (i+1,j+1) /= dem % nodata) THEN z9 = dem % mat (i+1,j+1) ELSE z9 = dem % mat (i,j) END IF ELSE z9 = dem % mat (i,j) END IF !set z8 IF ( .NOT. IsOutOfGrid (i+1,j,dem) ) THEN IF ( dem % mat (i+1,j) /= dem % nodata) THEN z8 = dem % mat (i+1,j) ELSE z8 = dem % mat (i,j) END IF ELSE z8 = dem % mat (i,j) END IF !set z7 IF ( .NOT. IsOutOfGrid (i+1,j-1,dem) ) THEN IF ( dem % mat (i+1,j-1) /= dem % nodata) THEN z7 = dem % mat (i+1,j-1) ELSE z7 = dem % mat (i,j) END IF ELSE z7 = dem % mat (i,j) END IF !set z4 IF ( .NOT. IsOutOfGrid (i,j-1,dem) ) THEN IF ( dem % mat (i,j-1) /= dem % nodata) THEN z4 = dem % mat (i,j-1) ELSE z4 = dem % mat (i,j) END IF ELSE z4 = dem % mat (i,j) END IF !set z1 IF ( .NOT. IsOutOfGrid (i-1,j-1,dem) ) THEN IF ( dem % mat (i-1,j-1) /= dem % nodata) THEN z1 = dem % mat (i-1,j-1) ELSE z1 = dem % mat (i,j) END IF ELSE z1 = dem % mat (i,j) END IF !compute D, E, F, G, H D = ((z4 + z6) /2. - z5) / L**2 E = ((z2 + z8) /2. - z5) / L**2 F = (-z1 + z3 + z7 - z9) / (4. * L**2) G = (-z4 + z6) / (2. * L) H = (z2 - z8) / (2. * L) curvature % mat (i,j) = -2. * (E + D) IF (PRESENT(profileCurvature)) THEN IF ( (G**2. + H**2.) > 0.) THEN profileCurvature % mat (i,j) = -2. * (D*G**2. + E*H**2. + F*G*H) / (G**2. + H**2.) ELSE profileCurvature % mat (i,j) = 0. END IF END IF IF (PRESENT(planCurvature)) THEN IF ( (G**2. + H**2.) > 0.) THEN planCurvature % mat (i,j) = -2. * (D*H**2. + E*G**2. - F*G*H) / (G**2. + H**2.) ELSE planCurvature % mat (i,j) = 0. END IF END IF END IF END DO jdim END DO idim RETURN END SUBROUTINE DeriveCurvature !============================================================================== !| Description: ! compute map of flow accumulation (m2) ! Input grid: flow direction SUBROUTINE FlowAccumulation & ! (fdir, facc) IMPLICIT NONE !arguments with intent in TYPE(grid_integer), INTENT(in):: fdir !arguments with intent out TYPE (grid_real), INTENT (out) :: facc !local variables INTEGER :: i,j !------------------------------end of declaration ----------------------------- !allocate new flow accumulation grid using flow direction grid as template CALL NewGrid (facc, fdir) !compute grid containing area of each cell DO i = 1, fdir % idim DO j = 1, fdir % jdim IF (fdir % mat (i,j) /= fdir % nodata) THEN facc % mat(i,j) = CellArea (facc, i, j) ELSE facc % mat(i,j) = facc % nodata END IF END DO END DO DO i = 1, fdir % idim DO j = 1, fdir % jdim IF (fdir % mat (i,j) /= fdir % nodata) THEN CALL BasinArea (i, j, fdir, facc % mat(i,j)) ELSE facc % mat(i,j) = facc % nodata END IF END DO END DO END SUBROUTINE FlowAccumulation !============================================================================== !| Description: ! compute basin area (m2) RECURSIVE SUBROUTINE BasinArea & ! (r, c, fdir, area) IMPLICIT NONE TYPE(grid_integer),INTENT(IN) :: fdir INTEGER, INTENt(in) :: r,c REAL (KIND = float), INTENT(inout) :: area !------------------------------end of declaration ----------------------------- IF ( .NOT. IsOutOfGrid(r,c+1,fdir) ) THEN IF(fdir%mat(r,c+1) == W) THEN area = area + CellArea (fdir, r, c+1) CALL BasinArea (r,c+1,fdir,area) END IF END IF IF ( .NOT. IsOutOfGrid(r+1,c+1,fdir) ) THEN IF(fdir%mat(r+1,c+1) == NW ) THEN area = area + CellArea (fdir, r+1, c+1) CALL BasinArea (r+1,c+1,fdir,area) END IF END IF IF ( .NOT. IsOutOfGrid(r+1,c,fdir) ) THEN IF(fdir%mat(r+1,c) == N ) THEN area = area + CellArea (fdir, r+1, c) CALL BasinArea (r+1,c,fdir,area) END IF END IF IF ( .NOT. IsOutOfGrid(r+1,c-1,fdir) ) THEN IF(fdir%mat(r+1,c-1) == NE ) THEN area = area + CellArea (fdir, r+1, c-1) CALL BasinArea (r+1,c-1,fdir,area) END IF END IF IF ( .NOT. IsOutOfGrid(r,c-1,fdir) ) THEN IF(fdir%mat(r,c-1) == E) THEN area = area + CellArea (fdir, r, c-1) CALL BasinArea (r,c-1,fdir,area) END IF END IF IF ( .NOT. IsOutOfGrid(r-1,c-1,fdir) ) THEN IF(fdir%mat(r-1,c-1) == SE ) THEN area = area + CellArea (fdir, r-1, c-1) CALL BasinArea (r-1,c-1,fdir,area) END IF END IF IF ( .NOT. IsOutOfGrid(r-1,c,fdir) ) THEN IF(fdir%mat(r-1,c) == S ) THEN area = area + CellArea (fdir, r-1, c) CALL BasinArea (r-1,c,fdir,area) END IF END IF IF ( .NOT. IsOutOfGrid(r-1,c+1,fdir) ) THEN IF(fdir%mat(r-1,c+1) == SW ) THEN area = area + CellArea (fdir, r-1, c+1) CALL BasinArea (r-1,c+1,fdir,area) END IF END IF END SUBROUTINE BasinArea !============================================================================== !| Description: ! find the cells that are springs, defined as those cells that have not ! any other cells upstream FUNCTION CellIsSpring & ! (row, col, flowDir) & ! RESULT (spring) IMPLICIT NONE ! Arguments with intent(in): INTEGER, INTENT(in) :: row, col TYPE (grid_integer), INTENT(in) :: flowDir ! Local variables: LOGICAL :: spring !------------end of declaration------------------------------------------------ spring = .TRUE. IF (flowDir % mat(row,col) == flowDir % nodata) THEN spring = .FALSE. RETURN END IF IF(.NOT. IsOutOfGrid(row,col+1,flowDir) ) THEN IF(flowDir%mat(row,col+1) == W ) THEN spring = .FALSE. RETURN ENDIF ENDIF IF(.NOT. IsOutOfGrid(row+1,col+1,flowDir) ) THEN IF(flowDir%mat(row+1,col+1) == NW ) THEN spring = .FALSE. RETURN ENDIF ENDIF IF(.NOT. IsOutOfGrid(row+1,col,flowDir) ) THEN IF(flowDir%mat(row+1,col) == N ) THEN spring = .FALSE. RETURN ENDIF ENDIF IF(.NOT. IsOutOfGrid(row+1,col-1,flowDir) ) THEN IF(flowDir%mat(row+1,col-1) == NE ) THEN spring = .FALSE. RETURN ENDIF ENDIF IF(.NOT. IsOutOfGrid(row,col-1,flowDir) ) THEN IF(flowDir%mat(row,col-1) == E ) THEN spring = .FALSE. RETURN ENDIF ENDIF IF(.NOT. IsOutOfGrid(row-1,col-1,flowDir) ) THEN IF(flowDir%mat(row-1,col-1) == SE ) THEN spring = .FALSE. RETURN ENDIF ENDIF IF(.NOT. IsOutOfGrid(row-1,col,flowDir) ) THEN IF(flowDir%mat(row-1,col) == S ) THEN spring = .FALSE. RETURN ENDIF ENDIF IF(.NOT. IsOutOfGrid(row-1,col+1,flowDir) ) THEN IF(flowDir%mat(row-1,col+1) == SW ) THEN spring = .FALSE. RETURN ENDIF ENDIF END FUNCTION CellIsSpring !============================================================================== !| Description: ! compute mask of river basin given map of flow direction and the coordinate ! of the outlet point SUBROUTINE BasinDelineate & ! (fdir,x,y, mask) IMPLICIT NONE !arguments with intent in TYPE(grid_integer), INTENT(in):: fdir REAL (KIND = float), INTENT(in) :: x, y !!coordinate of outlet !arguments with intent inout: TYPE(grid_integer), INTENT(inout) :: mask !local variables INTEGER :: i,j LOGICAL :: check !------------------------------end of declaration ----------------------------- IF (.NOT. ALLOCATED (mask % mat)) THEN !allocate new grid CALL NewGrid (mask, fdir) END IF mask % mat = mask % nodata !compute row and column of outlet CALL GetIJ (x, y, fdir, i, j, check) !compute basin mask CALL BasinMask(mask,fdir,i,j) END SUBROUTINE BasinDelineate !============================================================================== !| Description: ! search for cells included in the river basin RECURSIVE SUBROUTINE BasinMask & ! (basin, fdir, r, c) IMPLICIT NONE TYPE(grid_integer),INTENT(IN) :: fdir TYPE(grid_integer),INTENT(INOUT) :: basin INTEGER, INTENT(in) :: r,c !------------------------------end of declaration ----------------------------- IF ( .NOT. IsOutOfGrid(r,c+1,fdir) ) THEN IF(fdir%mat(r,c+1)==W.AND. basin%mat(r,c+1)/=1) THEN basin%mat(r,c+1) = 1 CALL BasinMask(basin,fdir,r,c+1) END IF END IF IF ( .NOT. IsOutOfGrid(r+1,c+1,fdir) ) THEN IF(fdir%mat(r+1,c+1)==NW .AND. basin%mat(r+1,c+1)/=1) THEN basin%mat(r+1,c+1) = 1 CALL BasinMask(basin,fdir,r+1,c+1) END IF END IF IF ( .NOT. IsOutOfGrid(r+1,c,fdir) ) THEN IF(fdir%mat(r+1,c)==N .AND. basin%mat(r+1,c)/=1) THEN basin%mat(r+1,c) = 1 CALL BasinMask(basin,fdir,r+1,c) END IF END IF IF ( .NOT. IsOutOfGrid(r+1,c-1,fdir) ) THEN IF(fdir%mat(r+1,c-1)==NE .AND. basin%mat(r+1,c-1)/=1) THEN basin%mat(r+1,c-1) = 1 CALL BasinMask(basin,fdir,r+1,c-1) END IF END IF IF ( .NOT. IsOutOfGrid(r,c-1,fdir) ) THEN IF(fdir%mat(r,c-1)==E .AND. basin%mat(r,c-1)/=1) THEN basin%mat(r,c-1) = 1 CALL BasinMask(basin,fdir,r,c-1) END IF END IF IF ( .NOT. IsOutOfGrid(r-1,c-1,fdir) ) THEN IF(fdir%mat(r-1,c-1)==SE .AND. basin%mat(r-1,c-1)/=1) THEN basin%mat(r-1,c-1) = 1 CALL BasinMask(basin,fdir,r-1,c-1) END IF END IF IF ( .NOT. IsOutOfGrid(r-1,c,fdir) ) THEN IF(fdir%mat(r-1,c)==S .AND. basin%mat(r-1,c)/=1) THEN basin%mat(r-1,c) = 1 CALL BasinMask(basin,fdir,r-1,c) END IF END IF IF ( .NOT. IsOutOfGrid(r-1,c+1,fdir) ) THEN IF(fdir%mat(r-1,c+1)==SW .AND. basin%mat(r-1,c+1)/=1) THEN basin%mat(r-1,c+1) = 1 CALL BasinMask(basin,fdir,r-1,c+1) END IF END IF END SUBROUTINE BasinMask !============================================================================== !| Description: ! compute longest flow length (m) FUNCTION LongestFlowLength & ! (fdir, x, y) & ! RESULT (lmax) IMPLICIT NONE !Arguments with intent (in) TYPE(grid_integer),INTENT(IN) :: fdir REAL (KIND = float), INTENT(in) :: x, y !local declarations REAL (KIND = float) :: lmax REAL (KIND = float) :: length REAL (KIND = float) :: xu, yu, xd, yd TYPE(grid_integer) :: basin INTEGER (KIND = short) :: row, col !!current cell INTEGER (KIND = short) :: iDown, jDown !!downstream cell INTEGER (KIND = short) :: i, j LOGICAL :: outlet !------------------------------end of declaration ----------------------------- !delineate river basin CALL BasinDelineate (fdir, x, y, basin) !overlay flow direction map CALL GridResample (fdir, basin) point1 % system = basin % grid_mapping point2 % system = basin % grid_mapping lmax = 0. !loop trough basin DO j = 1,basin % jdim DO i = 1,basin % idim IF (basin % mat (i,j) /= basin % nodata) THEN IF(CellIsSpring(i,j,basin)) THEN !found a spring length = 0. !follow the reach till basin outlet row = i col = j outlet = .FALSE. DO WHILE (.NOT. outlet) ! follow the reach till the basin outlet CALL DownstreamCell(row, col, basin%mat(row,col), iDown, jDown) CALL GetXY (row,col,basin,xu,yu) CALL GetXY (iDown,jDown,basin,xd,yd) point1 % northing = yu point1 % easting = xu point2 % northing = yd point2 % easting = xd length = length + Distance(point1,point2) outlet = CheckOutlet(row,col,iDown,jDown,basin) IF (outlet) THEN IF (length > lmax) THEN lmax = length END IF END IF !loop row = iDown col = jDown END DO ENDIF END IF ENDDO ENDDO RETURN END FUNCTION LongestFlowLength !============================================================================== !| Description: ! compute slope of longest flow path (m/m) FUNCTION LongestFlowPathSlope & ! (fdir, dem, x, y, outsection, headsection) & ! RESULT (slope) IMPLICIT NONE !Arguments with intent (in) TYPE(grid_integer),INTENT(IN) :: fdir !flow direction TYPE(grid_real),INTENT(IN) :: dem !digital elevation model REAL (KIND = float), INTENT(in) :: x, y !Arguments with intent out TYPE (Coordinate), OPTIONAL, INTENT (OUT) :: outsection !!coordinate of outlet section TYPE (Coordinate), OPTIONAL, INTENT (OUT) :: headsection !!coordinate of head section !local declarations REAL (KIND = float) :: slope REAL (KIND = float) :: lmax REAL (KIND = float) :: length REAL (KIND = float) :: xu, yu, xd, yd TYPE(grid_integer) :: basin INTEGER (KIND = short) :: row, col !!current cell INTEGER (KIND = short) :: iDown, jDown !!downstream cell INTEGER (KIND = short) :: iSpring, jSpring !!coordinate of the spring INTEGER (KIND = short) :: iHead, jHead !!coordinate of the head of the longest flow path INTEGER (KIND = short) :: iOutlet, jOutlet !!coordinate of the basin outlet INTEGER (KIND = short) :: i, j LOGICAL :: outlet !------------------------------end of declaration ----------------------------- !delineate river basin CALL BasinDelineate (fdir, x, y, basin) !overlay flow direction map CALL GridResample (fdir, basin) !find local coordinate of basin outlet CALL GetIJ (x, y, fdir, iOutlet, jOutlet) point1 % system = basin % grid_mapping point2 % system = basin % grid_mapping lmax = 0. iHead = 0 jHead = 0 !loop trough basin DO j = 1,basin % jdim DO i = 1,basin % idim IF (basin % mat (i,j) /= basin % nodata) THEN IF(CellIsSpring(i,j,basin)) THEN !found a spring length = 0. iSpring = i jSpring = j !follow the reach till basin outlet row = i col = j outlet = .FALSE. DO WHILE (.NOT. outlet) ! follow the reach till the basin outlet CALL DownstreamCell(row, col, basin%mat(row,col), iDown, jDown) CALL GetXY (row,col,basin,xu,yu) CALL GetXY (iDown,jDown,basin,xd,yd) point1 % northing = yu point1 % easting = xu point2 % northing = yd point2 % easting = xd length = length + Distance(point1,point2) outlet = CheckOutlet(row,col,iDown,jDown,basin) IF (outlet) THEN IF (length > lmax) THEN !update lmax lmax = length !update head iHead = iSpring jHead = jSpring END IF END IF !loop row = iDown col = jDown END DO ENDIF END IF ENDDO ENDDO !compute slope (m/m) slope = ( dem % mat(iHead,jHead) - dem % mat (iOutlet,jOutlet) ) / lmax IF (PRESENT(outsection)) THEN outsection % system = basin % grid_mapping CALL GetXY (iOutlet, jOutlet, basin, outsection % easting, outsection % northing) END IF IF (PRESENT(headsection)) THEN headsection % system = basin % grid_mapping CALL GetXY (iHead, jHead, basin, headsection % easting, headsection % northing) END IF RETURN END FUNCTION LongestFlowPathSlope !============================================================================== !| Description: ! Drainage Density (1/m) of a basin is the total line length of all streams ! divided by basin area. If flow accumulation is not given, it is computed ! from flow direction. If coordinate of closing section is not given ! drainage density is computed for the entire grid. FUNCTION DrainageDensity & ! (channel, fdir, x, y, flowAcc) & ! RESULT (dd) IMPLICIT NONE !Arguments with intent (in) TYPE(grid_integer),INTENT(IN) :: channel !!mask of channel cells. NoData for non channel cells TYPE(grid_integer),INTENT(IN) :: fdir !!flow direction REAL (KIND = float), INTENT(IN),OPTIONAL :: x, y !!coordinate of basin closing section TYPE (grid_real),INTENT(IN),OPTIONAL :: flowAcc !!flow accumulation !local declarations REAL (KIND = float) :: dd TYPE (grid_real) :: facc TYPE (grid_integer) :: mask INTEGER (KIND = short) :: i,j INTEGER (KIND = short) :: is, js !!coordinate of downstream cell in local reference system REAL (KIND = float) :: length !!total river length REAL (KIND = float) :: cLength !!cell length LOGICAL :: check !------------------------------end of declaration ----------------------------- !set flowaccumulation IF (PRESENT (flowAcc)) THEN CALL NewGrid (facc, fdir) facc = flowAcc ELSE CALL FlowAccumulation (fdir, facc) END IF IF (PRESENT(x) .AND. PRESENT(y)) THEN !delineate watershed mask CALL BasinDelineate (fdir,x,y, mask) ELSE !drainage density is computed on entire grid CALL NewGrid (mask, fdir) END IF !compute total channel length [m] length = 0. DO i = 1, mask % idim DO j = 1, mask % jdim IF (mask % mat (i,j) /= mask % nodata) THEN IF (channel % mat (i,j) /= channel % nodata) THEN CALL DownstreamCell (i, j, fdir % mat(i,j), is, js, dx = cLength, grid = fdir) length = length + cLength END IF END IF END DO END DO !compute drainage density [1/m] CALL GetIJ (x, y, fdir, i, j, check) IF (.NOT. check) THEN CALL Catch ('error', 'Morphology', 'Drainage density computation: ', & argument = 'closing section outside grid dimension') END IF IF (channel % mat(i,j) == channel % nodata) THEN CALL Catch ('error', 'Morphology', 'Drainage density computation: ', & argument = 'closing section on hillslope') END IF dd = length / facc % mat(i,j) !deallocate CALL GridDestroy (facc) CALL GridDestroy (mask) RETURN END FUNCTION DrainageDensity !============================================================================== !| Description: ! Topographic index `TI` takes into account both a local slope geometry ! and site location in the landscape combining data on slope steepness `S` ! and specific catchment area `As`: !``` ! TI = ln (As/S) !``` ! For grid structures the specific catchment area can be obtained by ! dividing the contributing area of the cell by the effective contour length. ! This is the length of contour line within the grid cell over which flow ! can pass. It is calculated as the length of the line through the grid ! cell centre and perpendicular to the aspect direction. ! ! References: ! ! P. J. J. DESMET & G. GOVERS (1996): Comparison of routing algorithms ! for digital elevation models and their implications for predicting ! ephemeral gullies, International Journal of Geographical ! Information Systems, 10:3, 311-331 SUBROUTINE TopographicIndex & ! (dem, fdir, facc, xcoord, ycoord, tivalue, tigrid) IMPLICIT NONE !Argumentd with intent(in): TYPE (grid_real), INTENT(IN) :: dem !!digital elevation model TYPE (grid_integer), INTENT(IN) :: fdir !!flow direction TYPE (grid_real), OPTIONAL, INTENT(IN) :: facc !!flow accumulation in cell unit REAL (KIND = float) , OPTIONAL, INTENT(IN) :: xcoord !! x coordinate of cell for computing index REAL (KIND = float) , OPTIONAL, INTENT(IN) :: ycoord !! y coordinate of cell for computing index !Arguments with intent(out): REAL (KIND = float) , OPTIONAL, INTENT(OUT) :: tivalue !! index of cell with coordinates (x,y) TYPE (grid_real), OPTIONAL, INTENT(OUT) :: tigrid !!topographic index !local declarations TYPE (grid_real) :: area !! contributing area (m^2) TYPE (grid_real) :: aspect !!aspect (rad) TYPE (grid_real) :: slope !!slope (rad) REAL (KIND = float) :: as !! specific catchment area (m^2 / m) REAL (KIND = float) :: x !!factor converting cellsize along direction... !!...perpendicular to the aspect direction REAL (KIND = float) :: alpha !! local aspect (rad) REAL (KIND = float) :: cellsize !!cell size (m) INTEGER (KIND = short) :: i, j !------------------------------end of declaration ----------------------------- !allocate topographic index grid CALL NewGrid (tigrid, dem) IF ( .NOT. PRESENT(facc) ) THEN !compute contributing area CALL FlowAccumulation (fdir, area) ELSE CALL NewGrid (area, fdir) DO i = 1, area % idim DO j = 1, area % jdim IF (area % mat (i,j) /= area % nodata ) THEN area % mat (i,j) = facc % mat (i,j) ! (* CellArea (facc, i, j)) END IF END DO END DO END IF !compute aspect CALL DeriveAspect (dem, aspect) !compute slope CALL DeriveSlope (dem, slope) !if present tivalue compute topographic index only for cell with coordinate xcoord, ycoord IF (PRESENT (tivalue) ) THEN !check wether coordinates of cell is given IF ( PRESENT (xcoord) .AND. PRESENT (ycoord) ) THEN CALL GetIJ (xcoord, ycoord, fdir, i, j) alpha = aspect % mat (i,j) x = 1. / MAX(ABS(SIN(alpha)),ABS(COS(alpha))) cellsize = ( CellArea (fdir, i, j) ) ** 0.5 ![m] as = area % mat (i,j) / (cellsize * x) IF ( TAN (slope % mat (i,j)) == 0.) THEN tivalue = LOG (as / 0.0001 ) ELSE tivalue = LOG (as / TAN (slope % mat (i,j))) END IF ELSE CALL Catch ('error', 'Morphology', 'missing coordinate for compuing topographic index of given cell') END IF END IF !If present tigrid, compute topographic index for every cell of the grid IF (PRESENT (tigrid) ) THEN DO i = 1, tigrid % idim DO j = 1, tigrid % jdim IF (tigrid % mat (i,j) /= tigrid % nodata) THEN alpha = aspect % mat (i,j) x = 1. / MAX(ABS(SIN(alpha)),ABS(COS(alpha))) cellsize = ( CellArea (tigrid, i, j) ) ** 0.5 ![m] as = area % mat (i,j) / (cellsize * x) IF ( TAN (slope % mat (i,j)) == 0.) THEN tigrid % mat (i,j) = LOG (as / 0.0001) ELSE tigrid % mat (i,j) = LOG (as / TAN (slope % mat (i,j))) END IF END IF END DO END DO END IF !purge CALL GridDestroy (area) CALL GridDestroy (aspect) CALL GridDestroy (slope) RETURN END SUBROUTINE TopographicIndex !============================================================================== !| Description: ! find the cells that are springs, defined as those cells that have not ! any other cells upstream FUNCTION Perimeter & ! (grid) & ! RESULT (p) IMPLICIT NONE ! Arguments with intent(in): TYPE (grid_integer), INTENT(IN) :: grid ! Local variables: REAL (KIND = float) :: p TYPE (grid_integer) :: border INTEGER :: i,j !------------end of declaration------------------------------------------------ CALL ExtractBorder (grid, border, cardinal = .TRUE.) p = 0. DO i = 1, border % idim DO j = 1, border % jdim IF (border % mat (i,j) /= border % nodata ) THEN p = p + CellLength (border, i,j) END IF END DO END DO !increase a default value of 9% to consider diagonal length p = p * 1.09 CALL GridDestroy (border) RETURN END FUNCTION Perimeter END MODULE Morphology